/* eslint-disable @typescript-eslint/no-explicit-any */
/*
 * These are copied from `viem` (EIP1193Provider)
 * They have an issue with `node16`, we can not directly import from their package.
 * When they fixed the issue, it would better to re-export from viem instead of keeping it here and maintaining the changes.
 */

// These are types that has so much nested types, for now i put any instead.
type Block = any;
type Log = any;
type TransactionRequest = any;
type BlockNumber = any;
type BlockTag = any;
type BlockIdentifier = any;
type RpcStateOverride = any;
type FeeHistory = any;
type Proof = any;
type Transaction = any;
type TransactionReceipt = any;
type LogTopic = any;
type Uncle = any;
type RpcUserOperation<_ = any> = any;
type RpcEstimateUserOperationGasReturnType = any;
type RpcGetUserOperationByHashReturnType = any;
type RpcUserOperationReceipt = any;

type KeyofUnion<type> = type extends type ? keyof type : never;

export type OneOf<
  union extends object,
  fallback extends object | undefined = undefined,
  ///
  keys extends KeyofUnion<union> = KeyofUnion<union>
> = union extends infer item
  ? Prettify<
      item & {
        [key in Exclude<keys, keyof item>]?: fallback extends object
          ? key extends keyof fallback
            ? fallback[key]
            : undefined
          : undefined;
      }
    >
  : never;

export type AddEthereumChainParameter = {
  /** A 0x-prefixed hexadecimal string */
  chainId: string;
  /** The chain name. */
  chainName: string;
  /** Native currency for the chain. */
  nativeCurrency?:
    | {
        name: string;
        symbol: string;
        decimals: number;
      }
    | undefined;
  rpcUrls: readonly string[];
  blockExplorerUrls?: string[] | undefined;
  iconUrls?: string[] | undefined;
};

export type NetworkSync = {
  /** The current block number */
  currentBlock: Quantity;
  /** Number of latest block on the network */
  highestBlock: Quantity;
  /** Block number at which syncing started */
  startingBlock: Quantity;
};

export type WalletCapabilities = {
  [capability: string]: any;
};

export type WalletCapabilitiesRecord<
  capabilities extends WalletCapabilities = WalletCapabilities,
  id extends string | number = Hex
> = {
  [chainId in id]: capabilities;
};

export type WalletCallReceipt<quantity = Hex, status = Hex> = {
  logs: {
    address: Hex;
    data: Hex;
    topics: Hex[];
  }[];
  status: status;
  blockHash: Hex;
  blockNumber: quantity;
  gasUsed: quantity;
  transactionHash: Hex;
};

export type WalletGrantPermissionsParameters = {
  signer?:
    | {
        type: string;
        data?: unknown | undefined;
      }
    | undefined;
  permissions: readonly {
    data: unknown;
    policies: readonly {
      data: unknown;
      type: string;
    }[];
    required?: boolean | undefined;
    type: string;
  }[];
  expiry: number;
};

export type WalletGrantPermissionsReturnType = {
  expiry: number;
  factory?: `0x${string}` | undefined;
  factoryData?: string | undefined;
  grantedPermissions: readonly {
    data: unknown;
    policies: readonly {
      data: unknown;
      type: string;
    }[];
    required?: boolean | undefined;
    type: string;
  }[];
  permissionsContext: string;
  signerData?:
    | {
        userOpBuilder?: `0x${string}` | undefined;
        submitToAddress?: `0x${string}` | undefined;
      }
    | undefined;
};

export type WalletGetCallsStatusReturnType<quantity = Hex, status = Hex> = {
  status: 'PENDING' | 'CONFIRMED';
  receipts?: WalletCallReceipt<quantity, status>[] | undefined;
};

export type WalletPermissionCaveat = {
  type: string;
  value: any;
};

export type WalletPermission = {
  caveats: WalletPermissionCaveat[];
  date: number;
  id: string;
  invoker: `http://${string}` | `https://${string}`;
  parentCapability: 'eth_accounts' | string;
};

export type WalletSendCallsParameters<
  capabilities extends WalletCapabilities = WalletCapabilities,
  chainId extends Hex | number = Hex,
  quantity extends Quantity | bigint = Quantity
> = [
  {
    calls: OneOf<
      | {
          to: Address;
          data?: Hex | undefined;
          value?: quantity | undefined;
        }
      | {
          data: Hex;
        }
    >[];
    capabilities?: capabilities | undefined;
    chainId: chainId;
    from: Address;
    version: string;
  }
];

export type WatchAssetParams = {
  /** Token type. */
  type: 'ERC20';
  options: {
    /** The address of the token contract */
    address: string;
    /** A ticker symbol or shorthand, up to 11 characters */
    symbol: string;
    /** The number of token decimals */
    decimals: number;
    /** A string url of the token logo */
    image?: string | undefined;
  };
};

export type ExactPartial<type> = {
  [key in keyof type]?: type[key] | undefined;
};

export type PartialBy<T, K extends keyof T> = Omit<T, K> &
  ExactPartial<Pick<T, K>>;

/** TypeScript type to use for `address` values */
type Address = `0x${string}`;

export type ProviderConnectInfo = {
  chainId: string;
};

export type ProviderMessage = {
  type: string;
  data: unknown;
};

class ProviderRpcError extends Error {
  code: number;
  details: string;

  constructor(code: number, message: string) {
    super(message);
    this.code = code;
    this.details = message;
  }
}

export type EIP1193EventMap = {
  accountsChanged(accounts: Address[]): void;
  chainChanged(chainId: string): void;
  connect(connectInfo: ProviderConnectInfo): void;
  disconnect(error: ProviderRpcError): void;
  message(message: ProviderMessage): void;
};

export type EIP1193Events = {
  on<event extends keyof EIP1193EventMap>(
    event: event,
    listener: EIP1193EventMap[event]
  ): void;
  removeListener<event extends keyof EIP1193EventMap>(
    event: event,
    listener: EIP1193EventMap[event]
  ): void;
};

export type Prettify<T> = {
  [K in keyof T]: T[K];
} & {};

export type RpcSchema = readonly {
  Method: string;
  Parameters?: unknown | undefined;
  ReturnType: unknown;
}[];

export type RpcSchemaOverride = Omit<RpcSchema[number], 'Method'>;

export type EIP1193Parameters<
  rpcSchema extends RpcSchema | undefined = undefined
> = rpcSchema extends RpcSchema
  ? {
      [K in keyof rpcSchema]: Prettify<
        {
          method: rpcSchema[K] extends rpcSchema[number]
            ? rpcSchema[K]['Method']
            : never;
        } & (rpcSchema[K] extends rpcSchema[number]
          ? rpcSchema[K]['Parameters'] extends undefined
            ? { params?: undefined }
            : { params: rpcSchema[K]['Parameters'] }
          : never)
      >;
    }[number]
  : {
      method: string;
      params?: unknown | undefined;
    };

export type EIP1193RequestOptions = {
  // Deduplicate in-flight requests.
  dedupe?: boolean | undefined;
  // The base delay (in ms) between retries.
  retryDelay?: number | undefined;
  // The max number of times to retry.
  retryCount?: number | undefined;
  /** Unique identifier for the request. */
  uid?: string | undefined;
};

type DerivedRpcSchema<
  rpcSchema extends RpcSchema | undefined,
  rpcSchemaOverride extends RpcSchemaOverride | undefined
> = rpcSchemaOverride extends RpcSchemaOverride
  ? [rpcSchemaOverride & { Method: string }]
  : rpcSchema;

export type EIP1193RequestFn<
  rpcSchema extends RpcSchema | undefined = undefined
> = <
  rpcSchemaOverride extends RpcSchemaOverride | undefined = undefined,
  _parameters extends EIP1193Parameters<
    DerivedRpcSchema<rpcSchema, rpcSchemaOverride>
  > = EIP1193Parameters<DerivedRpcSchema<rpcSchema, rpcSchemaOverride>>,
  _returnType = DerivedRpcSchema<rpcSchema, rpcSchemaOverride> extends RpcSchema
    ? Extract<
        DerivedRpcSchema<rpcSchema, rpcSchemaOverride>[number],
        { Method: _parameters['method'] }
      >['ReturnType']
    : unknown
>(
  args: _parameters,
  options?: EIP1193RequestOptions | undefined
) => Promise<_returnType>;

export type Hex = `0x${string}`;
export type Hash = `0x${string}`;
export type Quantity = `0x${string}`;

export type PublicRpcSchema = [
  /**
   * @description Returns the version of the current client
   *
   * @example
   * provider.request({ method: 'web3_clientVersion' })
   * // => 'MetaMask/v1.0.0'
   */
  {
    Method: 'web3_clientVersion';
    Parameters?: undefined;
    ReturnType: string;
  },
  /**
   * @description Hashes data using the Keccak-256 algorithm
   *
   * @example
   * provider.request({ method: 'web3_sha3', params: ['0x68656c6c6f20776f726c64'] })
   * // => '0xc94770007dda54cF92009BFF0dE90c06F603a09f'
   */
  {
    Method: 'web3_sha3';
    Parameters: [data: Hash];
    ReturnType: string;
  },
  /**
   * @description Determines if this client is listening for new network connections
   *
   * @example
   * provider.request({ method: 'net_listening' })
   * // => true
   */
  {
    Method: 'net_listening';
    Parameters?: undefined;
    ReturnType: boolean;
  },
  /**
   * @description Returns the number of peers currently connected to this client
   *
   * @example
   * provider.request({ method: 'net_peerCount' })
   * // => '0x1'
   */
  {
    Method: 'net_peerCount';
    Parameters?: undefined;
    ReturnType: Quantity;
  },
  /**
   * @description Returns the chain ID associated with the current network
   *
   * @example
   * provider.request({ method: 'net_version' })
   * // => '1'
   */
  {
    Method: 'net_version';
    Parameters?: undefined;
    ReturnType: Quantity;
  },
  /**
   * @description Returns the base fee per blob gas in wei.
   *
   * @example
   * provider.request({ method: 'eth_blobBaseFee' })
   * // => '0x09184e72a000'
   */
  {
    Method: 'eth_blobBaseFee';
    Parameters?: undefined;
    ReturnType: Quantity;
  },
  /**
   * @description Returns the number of the most recent block seen by this client
   *
   * @example
   * provider.request({ method: 'eth_blockNumber' })
   * // => '0x1b4'
   */
  {
    Method: 'eth_blockNumber';
    Parameters?: undefined;
    ReturnType: Quantity;
  },
  /**
   * @description Executes a new message call immediately without submitting a transaction to the network
   *
   * @example
   * provider.request({ method: 'eth_call', params: [{ to: '0x...', data: '0x...' }] })
   * // => '0x...'
   */
  {
    Method: 'eth_call';
    Parameters:
      | [transaction: ExactPartial<TransactionRequest>]
      | [
          transaction: ExactPartial<TransactionRequest>,
          block: BlockNumber | BlockTag | BlockIdentifier
        ]
      | [
          transaction: ExactPartial<TransactionRequest>,
          block: BlockNumber | BlockTag | BlockIdentifier,
          stateOverrideSet: RpcStateOverride
        ];
    ReturnType: Hex;
  },
  /**
   * @description Returns the chain ID associated with the current network
   * @example
   * provider.request({ method: 'eth_chainId' })
   * // => '1'
   */
  {
    Method: 'eth_chainId';
    Parameters?: undefined;
    ReturnType: Quantity;
  },
  /**
   * @description Returns the client coinbase address.
   * @example
   * provider.request({ method: 'eth_coinbase' })
   * // => '0x...'
   */
  {
    Method: 'eth_coinbase';
    Parameters?: undefined;
    ReturnType: Address;
  },
  /**
   * @description Estimates the gas necessary to complete a transaction without submitting it to the network
   *
   * @example
   * provider.request({
   *  method: 'eth_estimateGas',
   *  params: [{ from: '0x...', to: '0x...', value: '0x...' }]
   * })
   * // => '0x5208'
   */
  {
    Method: 'eth_estimateGas';
    Parameters:
      | [transaction: TransactionRequest]
      | [transaction: TransactionRequest, block: BlockNumber | BlockTag]
      | [
          transaction: TransactionRequest,
          block: BlockNumber | BlockTag,
          stateOverride: RpcStateOverride
        ];
    ReturnType: Quantity;
  },
  /**
   * @description Returns a collection of historical gas information
   *
   * @example
   * provider.request({
   *  method: 'eth_feeHistory',
   *  params: ['4', 'latest', ['25', '75']]
   * })
   * // => {
   * //   oldestBlock: '0x1',
   * //   baseFeePerGas: ['0x1', '0x2', '0x3', '0x4'],
   * //   gasUsedRatio: ['0x1', '0x2', '0x3', '0x4'],
   * //   reward: [['0x1', '0x2'], ['0x3', '0x4'], ['0x5', '0x6'], ['0x7', '0x8']]
   * // }
   *
   */
  {
    Method: 'eth_feeHistory';
    Parameters: [
      /** Number of blocks in the requested range. Between 1 and 1024 blocks can be requested in a single query. Less than requested may be returned if not all blocks are available. */
      blockCount: Quantity,
      /** Highest number block of the requested range. */
      newestBlock: BlockNumber | BlockTag,
      /** A monotonically increasing list of percentile values to sample from each block's effective priority fees per gas in ascending order, weighted by gas used. */
      rewardPercentiles: number[] | undefined
    ];
    ReturnType: FeeHistory;
  },
  /**
   * @description Returns the current price of gas expressed in wei
   *
   * @example
   * provider.request({ method: 'eth_gasPrice' })
   * // => '0x09184e72a000'
   */
  {
    Method: 'eth_gasPrice';
    Parameters?: undefined;
    ReturnType: Quantity;
  },
  /**
   * @description Returns the balance of an address in wei
   *
   * @example
   * provider.request({ method: 'eth_getBalance', params: ['0x...', 'latest'] })
   * // => '0x12a05...'
   */
  {
    Method: 'eth_getBalance';
    Parameters: [
      address: Address,
      block: BlockNumber | BlockTag | BlockIdentifier
    ];
    ReturnType: Quantity;
  },
  /**
   * @description Returns information about a block specified by hash
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getBlockByHash', params: ['0x...', true] })
   * // => {
   * //   number: '0x1b4',
   * //   hash: '0x...',
   * //   parentHash: '0x...',
   * //   ...
   * // }
   */
  {
    Method: 'eth_getBlockByHash';
    Parameters: [
      /** hash of a block */
      hash: Hash,
      /** true will pull full transaction objects, false will pull transaction hashes */
      includeTransactionObjects: boolean
    ];
    ReturnType: Block | null;
  },
  /**
   * @description Returns information about a block specified by number
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getBlockByNumber', params: ['0x1b4', true] })
   * // => {
   * //   number: '0x1b4',
   * //   hash: '0x...',
   * //   parentHash: '0x...',
   * //   ...
   * // }
   */
  {
    Method: 'eth_getBlockByNumber';
    Parameters: [
      /** block number, or one of "latest", "safe", "finalized", "earliest" or "pending" */
      block: BlockNumber | BlockTag,
      /** true will pull full transaction objects, false will pull transaction hashes */
      includeTransactionObjects: boolean
    ];
    ReturnType: Block | null;
  },
  /**
   * @description Returns the number of transactions in a block specified by block hash
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getBlockTransactionCountByHash', params: ['0x...'] })
   * // => '0x1'
   */
  {
    Method: 'eth_getBlockTransactionCountByHash';
    Parameters: [hash: Hash];
    ReturnType: Quantity;
  },
  /**
   * @description Returns the number of transactions in a block specified by block number
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getBlockTransactionCountByNumber', params: ['0x1b4'] })
   * // => '0x1'
   */
  {
    Method: 'eth_getBlockTransactionCountByNumber';
    Parameters: [block: BlockNumber | BlockTag];
    ReturnType: Quantity;
  },
  /**
   * @description Returns the contract code stored at a given address
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getCode', params: ['0x...', 'latest'] })
   * // => '0x...'
   */
  {
    Method: 'eth_getCode';
    Parameters: [
      address: Address,
      block: BlockNumber | BlockTag | BlockIdentifier
    ];
    ReturnType: Hex;
  },
  /**
   * @description Returns a list of all logs based on filter ID since the last log retrieval
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getFilterChanges', params: ['0x...'] })
   * // => [{ ... }, { ... }]
   */
  {
    Method: 'eth_getFilterChanges';
    Parameters: [filterId: Quantity];
    ReturnType: Log[] | Hex[];
  },
  /**
   * @description Returns a list of all logs based on filter ID
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getFilterLogs', params: ['0x...'] })
   * // => [{ ... }, { ... }]
   */
  {
    Method: 'eth_getFilterLogs';
    Parameters: [filterId: Quantity];
    ReturnType: Log[];
  },
  /**
   * @description Returns a list of all logs based on a filter object
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getLogs', params: [{ fromBlock: '0x...', toBlock: '0x...', address: '0x...', topics: ['0x...'] }] })
   * // => [{ ... }, { ... }]
   */
  {
    Method: 'eth_getLogs';
    Parameters: [
      {
        address?: Address | Address[] | undefined;
        topics?: LogTopic[] | undefined;
      } & (
        | {
            fromBlock?: BlockNumber | BlockTag | undefined;
            toBlock?: BlockNumber | BlockTag | undefined;
            blockHash?: undefined;
          }
        | {
            fromBlock?: undefined;
            toBlock?: undefined;
            blockHash?: Hash | undefined;
          }
      )
    ];
    ReturnType: Log[];
  },
  /**
   * @description Returns the account and storage values of the specified account including the Merkle-proof.
   * @link https://eips.ethereum.org/EIPS/eip-1186
   * @example
   * provider.request({ method: 'eth_getProof', params: ['0x...', ['0x...'], 'latest'] })
   * // => {
   * //   ...
   * // }
   */
  {
    Method: 'eth_getProof';
    Parameters: [
      /** Address of the account. */
      address: Address,
      /** An array of storage-keys that should be proofed and included. */
      storageKeys: Hash[],
      block: BlockNumber | BlockTag
    ];
    ReturnType: Proof;
  },
  /**
   * @description Returns the value from a storage position at an address
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getStorageAt', params: ['0x...', '0x...', 'latest'] })
   * // => '0x...'
   */
  {
    Method: 'eth_getStorageAt';
    Parameters: [
      address: Address,
      index: Quantity,
      block: BlockNumber | BlockTag | BlockIdentifier
    ];
    ReturnType: Hex;
  },
  /**
   * @description Returns information about a transaction specified by block hash and transaction index
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getTransactionByBlockHashAndIndex', params: ['0x...', '0x...'] })
   * // => { ... }
   */
  {
    Method: 'eth_getTransactionByBlockHashAndIndex';
    Parameters: [hash: Hash, index: Quantity];
    ReturnType: Transaction | null;
  },
  /**
   * @description Returns information about a transaction specified by block number and transaction index
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getTransactionByBlockNumberAndIndex', params: ['0x...', '0x...'] })
   * // => { ... }
   */
  {
    Method: 'eth_getTransactionByBlockNumberAndIndex';
    Parameters: [block: BlockNumber | BlockTag, index: Quantity];
    ReturnType: Transaction | null;
  },
  /**
   * @description Returns information about a transaction specified by hash
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getTransactionByHash', params: ['0x...'] })
   * // => { ... }
   */
  {
    Method: 'eth_getTransactionByHash';
    Parameters: [hash: Hash];
    ReturnType: Transaction | null;
  },
  /**
   * @description Returns the number of transactions sent from an address
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getTransactionCount', params: ['0x...', 'latest'] })
   * // => '0x1'
   */
  {
    Method: 'eth_getTransactionCount';
    Parameters: [
      address: Address,
      block: BlockNumber | BlockTag | BlockIdentifier
    ];
    ReturnType: Quantity;
  },
  /**
   * @description Returns the receipt of a transaction specified by hash
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getTransactionReceipt', params: ['0x...'] })
   * // => { ... }
   */
  {
    Method: 'eth_getTransactionReceipt';
    Parameters: [hash: Hash];
    ReturnType: TransactionReceipt | null;
  },
  /**
   * @description Returns information about an uncle specified by block hash and uncle index position
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getUncleByBlockHashAndIndex', params: ['0x...', '0x...'] })
   * // => { ... }
   */
  {
    Method: 'eth_getUncleByBlockHashAndIndex';
    Parameters: [hash: Hash, index: Quantity];
    ReturnType: Uncle | null;
  },
  /**
   * @description Returns information about an uncle specified by block number and uncle index position
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getUncleByBlockNumberAndIndex', params: ['0x...', '0x...'] })
   * // => { ... }
   */
  {
    Method: 'eth_getUncleByBlockNumberAndIndex';
    Parameters: [block: BlockNumber | BlockTag, index: Quantity];
    ReturnType: Uncle | null;
  },
  /**
   * @description Returns the number of uncles in a block specified by block hash
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getUncleCountByBlockHash', params: ['0x...'] })
   * // => '0x1'
   */
  {
    Method: 'eth_getUncleCountByBlockHash';
    Parameters: [hash: Hash];
    ReturnType: Quantity;
  },
  /**
   * @description Returns the number of uncles in a block specified by block number
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_getUncleCountByBlockNumber', params: ['0x...'] })
   * // => '0x1'
   */
  {
    Method: 'eth_getUncleCountByBlockNumber';
    Parameters: [block: BlockNumber | BlockTag];
    ReturnType: Quantity;
  },
  /**
   * @description Returns the current maxPriorityFeePerGas in wei.
   * @link https://ethereum.github.io/execution-apis/api-documentation/
   * @example
   * provider.request({ method: 'eth_maxPriorityFeePerGas' })
   * // => '0x5f5e100'
   */
  {
    Method: 'eth_maxPriorityFeePerGas';
    Parameters?: undefined;
    ReturnType: Quantity;
  },
  /**
   * @description Creates a filter to listen for new blocks that can be used with `eth_getFilterChanges`
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_newBlockFilter' })
   * // => '0x1'
   */
  {
    Method: 'eth_newBlockFilter';
    Parameters?: undefined;
    ReturnType: Quantity;
  },
  /**
   * @description Creates a filter to listen for specific state changes that can then be used with `eth_getFilterChanges`
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_newFilter', params: [{ fromBlock: '0x...', toBlock: '0x...', address: '0x...', topics: ['0x...'] }] })
   * // => '0x1'
   */
  {
    Method: 'eth_newFilter';
    Parameters: [
      filter: {
        fromBlock?: BlockNumber | BlockTag | undefined;
        toBlock?: BlockNumber | BlockTag | undefined;
        address?: Address | Address[] | undefined;
        topics?: LogTopic[] | undefined;
      }
    ];
    ReturnType: Quantity;
  },
  /**
   * @description Creates a filter to listen for new pending transactions that can be used with `eth_getFilterChanges`
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_newPendingTransactionFilter' })
   * // => '0x1'
   */
  {
    Method: 'eth_newPendingTransactionFilter';
    Parameters?: undefined;
    ReturnType: Quantity;
  },
  /**
   * @description Returns the current Ethereum protocol version
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_protocolVersion' })
   * // => '54'
   */
  {
    Method: 'eth_protocolVersion';
    Parameters?: undefined;
    ReturnType: string;
  },
  /**
   * @description Sends a **signed** transaction to the network
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_sendRawTransaction', params: ['0x...'] })
   * // => '0x...'
   */
  {
    Method: 'eth_sendRawTransaction';
    Parameters: [signedTransaction: Hex];
    ReturnType: Hash;
  },
  /**
   * @description Destroys a filter based on filter ID
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_uninstallFilter', params: ['0x1'] })
   * // => true
   */
  {
    Method: 'eth_uninstallFilter';
    Parameters: [filterId: Quantity];
    ReturnType: boolean;
  }
];

export type WalletRpcSchema = [
  /**
   * @description Returns a list of addresses owned by this client
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_accounts' })
   * // => ['0x0fB69...']
   */
  {
    Method: 'eth_accounts';
    Parameters?: undefined;
    ReturnType: Address[];
  },
  /**
   * @description Returns the current chain ID associated with the wallet.
   * @example
   * provider.request({ method: 'eth_chainId' })
   * // => '1'
   */
  {
    Method: 'eth_chainId';
    Parameters?: undefined;
    ReturnType: Quantity;
  },
  /**
   * @description Estimates the gas necessary to complete a transaction without submitting it to the network
   *
   * @example
   * provider.request({
   *  method: 'eth_estimateGas',
   *  params: [{ from: '0x...', to: '0x...', value: '0x...' }]
   * })
   * // => '0x5208'
   */
  {
    Method: 'eth_estimateGas';
    Parameters:
      | [transaction: TransactionRequest]
      | [transaction: TransactionRequest, block: BlockNumber | BlockTag]
      | [
          transaction: TransactionRequest,
          block: BlockNumber | BlockTag,
          stateOverride: RpcStateOverride
        ];
    ReturnType: Quantity;
  },
  /**
   * @description Requests that the user provides an Ethereum address to be identified by. Typically causes a browser extension popup to appear.
   * @link https://eips.ethereum.org/EIPS/eip-1102
   * @example
   * provider.request({ method: 'eth_requestAccounts' }] })
   * // => ['0x...', '0x...']
   */
  {
    Method: 'eth_requestAccounts';
    Parameters?: undefined;
    ReturnType: Address[];
  },
  /**
   * @description Creates, signs, and sends a new transaction to the network
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_sendTransaction', params: [{ from: '0x...', to: '0x...', value: '0x...' }] })
   * // => '0x...'
   */
  {
    Method: 'eth_sendTransaction';
    Parameters: [transaction: TransactionRequest];
    ReturnType: Hash;
  },
  /**
   * @description Sends and already-signed transaction to the network
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_sendRawTransaction', params: ['0x...'] })
   * // => '0x...'
   */
  {
    Method: 'eth_sendRawTransaction';
    Parameters: [signedTransaction: Hex];
    ReturnType: Hash;
  },
  /**
   * @description Calculates an Ethereum-specific signature in the form of `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))`
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_sign', params: ['0x...', '0x...'] })
   * // => '0x...'
   */
  {
    Method: 'eth_sign';
    Parameters: [
      /** Address to use for signing */
      address: Address,
      /** Data to sign */
      data: Hex
    ];
    ReturnType: Hex;
  },
  /**
   * @description Signs a transaction that can be submitted to the network at a later time using with `eth_sendRawTransaction`
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_signTransaction', params: [{ from: '0x...', to: '0x...', value: '0x...' }] })
   * // => '0x...'
   */
  {
    Method: 'eth_signTransaction';
    Parameters: [request: TransactionRequest];
    ReturnType: Hex;
  },
  /**
   * @description Calculates an Ethereum-specific signature in the form of `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))`
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_signTypedData_v4', params: [{ from: '0x...', data: [{ type: 'string', name: 'message', value: 'hello world' }] }] })
   * // => '0x...'
   */
  {
    Method: 'eth_signTypedData_v4';
    Parameters: [
      /** Address to use for signing */
      address: Address,
      /** Message to sign containing type information, a domain separator, and data */
      message: string
    ];
    ReturnType: Hex;
  },
  /**
   * @description Returns information about the status of this client’s network synchronization
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'eth_syncing' })
   * // => { startingBlock: '0x...', currentBlock: '0x...', highestBlock: '0x...' }
   */
  {
    Method: 'eth_syncing';
    Parameters?: undefined;
    ReturnType: NetworkSync | false;
  },
  /**
   * @description Calculates an Ethereum-specific signature in the form of `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))`
   * @link https://eips.ethereum.org/EIPS/eip-1474
   * @example
   * provider.request({ method: 'personal_sign', params: ['0x...', '0x...'] })
   * // => '0x...'
   */
  {
    Method: 'personal_sign';
    Parameters: [
      /** Data to sign */
      data: Hex,
      /** Address to use for signing */
      address: Address
    ];
    ReturnType: Hex;
  },
  /**
   * @description Add an Ethereum chain to the wallet.
   * @link https://eips.ethereum.org/EIPS/eip-3085
   * @example
   * provider.request({ method: 'wallet_addEthereumChain', params: [{ chainId: 1, rpcUrl: 'https://mainnet.infura.io/v3/...' }] })
   * // => { ... }
   */
  {
    Method: 'wallet_addEthereumChain';
    Parameters: [chain: AddEthereumChainParameter];
    ReturnType: null;
  },
  /**
   * @description Returns the status of a call batch that was sent via `wallet_sendCalls`.
   * @link https://eips.ethereum.org/EIPS/eip-5792
   * @example
   * provider.request({ method: 'wallet_getCallsStatus' })
   * // => { ... }
   */
  {
    Method: 'wallet_getCallsStatus';
    Parameters?: [string];
    ReturnType: WalletGetCallsStatusReturnType;
  },
  /**
   * @description Gets the connected wallet's capabilities.
   * @link https://eips.ethereum.org/EIPS/eip-5792
   * @example
   * provider.request({ method: 'wallet_getCapabilities' })
   * // => { ... }
   */
  {
    Method: 'wallet_getCapabilities';
    Parameters?: [Address];
    ReturnType: Prettify<WalletCapabilitiesRecord>;
  },
  /**
   * @description Gets the wallets current permissions.
   * @link https://eips.ethereum.org/EIPS/eip-2255
   * @example
   * provider.request({ method: 'wallet_getPermissions' })
   * // => { ... }
   */
  {
    Method: 'wallet_getPermissions';
    Parameters?: undefined;
    ReturnType: WalletPermission[];
  },
  /**
   * @description Requests permissions from a wallet
   * @link https://eips.ethereum.org/EIPS/eip-7715
   * @example
   * provider.request({ method: 'wallet_grantPermissions', params: [{ ... }] })
   * // => { ... }
   */
  {
    Method: 'wallet_grantPermissions';
    Parameters?: [WalletGrantPermissionsParameters];
    ReturnType: Prettify<WalletGrantPermissionsReturnType>;
  },
  /**
   * @description Requests the given permissions from the user.
   * @link https://eips.ethereum.org/EIPS/eip-2255
   * @example
   * provider.request({ method: 'wallet_requestPermissions', params: [{ eth_accounts: {} }] })
   * // => { ... }
   */
  {
    Method: 'wallet_requestPermissions';
    Parameters: [permissions: { eth_accounts: Record<string, any> }];
    ReturnType: WalletPermission[];
  },
  /**
   * @description Revokes the given permissions from the user.
   * @link https://github.com/MetaMask/metamask-improvement-proposals/blob/main/MIPs/mip-2.md
   * @example
   * provider.request({ method: 'wallet_revokePermissions', params: [{ eth_accounts: {} }] })
   * // => { ... }
   */
  {
    Method: 'wallet_revokePermissions';
    Parameters: [permissions: { eth_accounts: Record<string, any> }];
    ReturnType: null;
  },
  /**
   * @description Requests the connected wallet to send a batch of calls.
   * @link https://eips.ethereum.org/EIPS/eip-5792
   * @example
   * provider.request({ method: 'wallet_sendCalls' })
   * // => { ... }
   */
  {
    Method: 'wallet_sendCalls';
    Parameters?: WalletSendCallsParameters;
    ReturnType: string;
  },
  /**
   * @description Requests for the wallet to show information about a call batch
   * that was sent via `wallet_sendCalls`.
   * @link https://eips.ethereum.org/EIPS/eip-5792
   * @example
   * provider.request({ method: 'wallet_showCallsStatus', params: ['...'] })
   */
  {
    Method: 'wallet_showCallsStatus';
    Parameters?: [string];
    ReturnType: void;
  },
  /**
   * @description Switch the wallet to the given Ethereum chain.
   * @link https://eips.ethereum.org/EIPS/eip-3326
   * @example
   * provider.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0xf00' }] })
   * // => { ... }
   */
  {
    Method: 'wallet_switchEthereumChain';
    Parameters: [chain: { chainId: string }];
    ReturnType: null;
  },
  /**
   * @description Requests that the user tracks the token in their wallet. Returns a boolean indicating if the token was successfully added.
   * @link https://eips.ethereum.org/EIPS/eip-747
   * @example
   * provider.request({ method: 'wallet_watchAsset' }] })
   * // => true
   */
  {
    Method: 'wallet_watchAsset';
    Parameters: WatchAssetParams;
    ReturnType: boolean;
  }
];

export type BundlerRpcSchema = [
  /**
   * @description Returns the chain ID associated with the current network
   *
   * @link https://eips.ethereum.org/EIPS/eip-4337#-eth_chainid
   */
  {
    Method: 'eth_chainId';
    Parameters?: undefined;
    ReturnType: Hex;
  },
  /**
   * @description Estimate the gas values for a UserOperation.
   *
   * @link https://eips.ethereum.org/EIPS/eip-4337#-eth_estimateuseroperationgas
   *
   * @example
   * provider.request({
   *  method: 'eth_estimateUserOperationGas',
   *  params: [{ ... }]
   * })
   * // => { ... }
   */
  {
    Method: 'eth_estimateUserOperationGas';
    Parameters:
      | [userOperation: RpcUserOperation, entrypoint: Address]
      | [
          userOperation: RpcUserOperation,
          entrypoint: Address,
          stateOverrideSet: RpcStateOverride
        ];
    ReturnType: RpcEstimateUserOperationGasReturnType;
  },
  /**
   * @description Return a UserOperation based on a hash.
   *
   * @link https://eips.ethereum.org/EIPS/eip-4337#-eth_getuseroperationbyhash
   *
   * @example
   * provider.request({
   *  method: 'eth_getUserOperationByHash',
   *  params: ['0x...']
   * })
   * // => { ... }
   */
  {
    Method: 'eth_getUserOperationByHash';
    Parameters: [hash: Hash];
    ReturnType: RpcGetUserOperationByHashReturnType | null;
  },
  /**
   * @description Return a UserOperation receipt based on a hash.
   *
   * @link https://eips.ethereum.org/EIPS/eip-4337#-eth_getuseroperationreceipt
   *
   * @example
   * provider.request({
   *  method: 'eth_getUserOperationReceipt',
   *  params: ['0x...']
   * })
   * // => { ... }
   */
  {
    Method: 'eth_getUserOperationReceipt';
    Parameters: [hash: Hash];
    ReturnType: RpcUserOperationReceipt | null;
  },
  /**
   * @description Submits a User Operation object to the User Operation pool of the client.
   *
   * @link https://eips.ethereum.org/EIPS/eip-4337#-eth_senduseroperation
   *
   * @example
   * provider.request({
   *  method: 'eth_sendUserOperation',
   *  params: [{ ... }]
   * })
   * // => '0x...'
   */
  {
    Method: 'eth_sendUserOperation';
    Parameters: [userOperation: RpcUserOperation, entrypoint: Address];
    ReturnType: Hash;
  },
  /**
   * @description Return the list of supported entry points by the client.
   *
   * @link https://eips.ethereum.org/EIPS/eip-4337#-eth_supportedentrypoints
   */
  {
    Method: 'eth_supportedEntryPoints';
    Parameters?: undefined;
    ReturnType: readonly Address[];
  }
];

export type PaymasterRpcSchema = [
  /**
   * @description Returns the chain ID associated with the current network
   *
   * @link https://eips.ethereum.org/EIPS/eip-4337#-eth_chainid
   */
  {
    Method: 'pm_getPaymasterStubData';
    Parameters?: [
      userOperation: OneOf<
        | PartialBy<
            Pick<
              RpcUserOperation<'0.6'>,
              | 'callData'
              | 'callGasLimit'
              | 'initCode'
              | 'maxFeePerGas'
              | 'maxPriorityFeePerGas'
              | 'nonce'
              | 'sender'
              | 'preVerificationGas'
              | 'verificationGasLimit'
            >,
            | 'callGasLimit'
            | 'initCode'
            | 'maxFeePerGas'
            | 'maxPriorityFeePerGas'
            | 'preVerificationGas'
            | 'verificationGasLimit'
          >
        | PartialBy<
            Pick<
              RpcUserOperation<'0.7'>,
              | 'callData'
              | 'callGasLimit'
              | 'factory'
              | 'factoryData'
              | 'maxFeePerGas'
              | 'maxPriorityFeePerGas'
              | 'nonce'
              | 'sender'
              | 'preVerificationGas'
              | 'verificationGasLimit'
            >,
            | 'callGasLimit'
            | 'factory'
            | 'factoryData'
            | 'maxFeePerGas'
            | 'maxPriorityFeePerGas'
            | 'preVerificationGas'
            | 'verificationGasLimit'
          >
      >,
      entrypoint: Address,
      chainId: Hex,
      context: unknown
    ];
    ReturnType: OneOf<
      | { paymasterAndData: Hex }
      | {
          paymaster: Address;
          paymasterData: Hex;
          paymasterVerificationGasLimit: Hex;
          paymasterPostOpGasLimit: Hex;
        }
    > & {
      sponsor?: { name: string; icon?: string | undefined } | undefined;
      isFinal?: boolean | undefined;
    };
  },
  /**
   * @description Returns values to be used in paymaster-related fields of a signed user operation.
   *
   * @link https://github.com/ethereum/ERCs/blob/master/ERCS/erc-7677.md#pm_getpaymasterdata
   */
  {
    Method: 'pm_getPaymasterData';
    Parameters?: [
      userOperation:
        | Pick<
            RpcUserOperation<'0.6'>,
            | 'callData'
            | 'callGasLimit'
            | 'initCode'
            | 'maxFeePerGas'
            | 'maxPriorityFeePerGas'
            | 'nonce'
            | 'sender'
            | 'preVerificationGas'
            | 'verificationGasLimit'
          >
        | Pick<
            RpcUserOperation<'0.7'>,
            | 'callData'
            | 'callGasLimit'
            | 'factory'
            | 'factoryData'
            | 'maxFeePerGas'
            | 'maxPriorityFeePerGas'
            | 'nonce'
            | 'sender'
            | 'preVerificationGas'
            | 'verificationGasLimit'
          >,
      entrypoint: Address,
      chainId: Hex,
      context: unknown
    ];
    ReturnType: OneOf<
      | { paymasterAndData: Hex }
      | {
          paymaster: Address;
          paymasterData: Hex;
          paymasterVerificationGasLimit: Hex;
          paymasterPostOpGasLimit: Hex;
        }
    >;
  }
];

export type EIP1474Methods = [
  ...PublicRpcSchema,
  ...WalletRpcSchema,
  ...BundlerRpcSchema,
  ...PaymasterRpcSchema
];

export type EIP1193Provider = EIP1193Events & {
  request: EIP1193RequestFn<EIP1474Methods>;
  disconnect: () => void;
};
